/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T1 Processor File: rz3iu.cpp
* Copyright (c) 2006 Sun Microsystems, Inc.  All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
*
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* The above named program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ========== Copyright Header End ============================================
*/

#include <sys/types.h>

#include "rz3iu.h"
#include "rz_insttypes.h"

// #include "spix_sparc.h"

// union dcti_info_u gen_dcti_info(uint32_t instr, spix_sparc_iop_t iop)

// fixed in v3.18 - better classification of branch_never insts
union dcti_info_u gen_dcti_info(uint32_t instr)
{
    union dcti_info_u dinfo;
    dinfo.u32 = 0x0;

    // if (spix_sparc_iop_isdcti(iop)) {
    if (rz_is_dcti(instr)) {
        dinfo.flags.isdcti = true;
        // if (spix_sparc_iop_isbranch(iop)) {
        if (rz_is_branch(instr)) {
            dinfo.flags.isbranch = true;
            // if (spix_sparc_iop_iscbranch(iop)) {
            if (rz_is_cbranch(instr)) {
                dinfo.flags.iscbranch = true;
            } else { /* not cbranch */
                dinfo.flags.isubranch = true;
                // uint32_t b = (instr & UBRANCH_OPCODE_MASK);
                // if ((b == FBN_OPCODE_BITS) || (b == BN_OPCODE_BITS)) {
                if (rz_is_ubranch_never(instr)) {
                    dinfo.flags.isubranch_nottaken = true;
                }
            } /* cbranch? */

            dinfo.flags.annul_flag = (instr >> 29) & 1;

            // specific branch type and disp
            // if ((instr & BPcc_OPCODE_MASK) == BPcc_OPCODE_BITS) {
            if (rz_is_bpcc(instr)) {
                dinfo.flags.isBPcc = 1;
                // } else if ((instr & BPR_OPCODE_MASK) == BPR_OPCODE_BITS) {
            } else if (rz_is_bpr(instr)) {
                dinfo.flags.isBPR = 1;
                // } else if ((instr & FBfcc_OPCODE_MASK) == FBfcc_OPCODE_BITS) {
            } else if (rz_is_fbfcc(instr)) {
                dinfo.flags.isFBfcc = 1;
                // } else if ((instr & FBPfcc_OPCODE_MASK) == FBPfcc_OPCODE_BITS) {
            } else if (rz_is_fbpfcc(instr)) {
                dinfo.flags.isFBPfcc = 1;
                // } else if ((instr & Bicc_OPCODE_MASK) == Bicc_OPCODE_BITS) {
            } else if (rz_is_bicc(instr)) {
                dinfo.flags.isBicc = 1;
            } else {
                /* other branch instr? */
                fprintf(stderr, "ERROR: rz3iu: gen_dcti_info: invalid branch instr %08x\n", instr);
                dinfo.u32 = 0;
                return dinfo;
            }

        } else { /* not branch */
            // if ((instr & CALL_OPCODE_MASK) == CALL_OPCODE_BITS) {
            if (rz_is_call(instr)) {
                dinfo.flags.iscall = 1;
            } else {
                dinfo.flags.isindirect = 1;
                // is ret if rd=0, rs1=31, imm=8
                // is retl if rd=0, rs1=15, imm=8
                // is "call indirect" otherwise if rd is 15
                int32_t rs1 = (instr >> 14) & 0x1f;
                int32_t rd = (instr >> 25) & 0x1f;
                uint32_t simm13 = instr & 0x1fff;
                if ((rd == 0) && (simm13 == 0x8)) {
                    if (rs1 == 15) {
                        dinfo.flags.is_retl = 1;
                    } else if (rs1 == 31) {
                        dinfo.flags.is_ret = 1;
                    } else {
                        // do nothing
                    }
                } // else - do nothing
            }
        } // branch?
        // } else if ((iop == SPIX_SPARC_IOP_DONE)||(iop == SPIX_SPARC_IOP_RETRY)) {
    } else if (rz_is_done(instr) || rz_is_retry(instr)) {
        dinfo.flags.is_done_retry = 1;
    } else {
        // do nothing
    } // instr type?

    return dinfo;
} // gen_dcti_info


// BUG: in versions 3.17 and older, branch_never insts were counted
// as conditional branches and iscbranch was set, because of the
// way spix classifies these insts. this leads to an incorrect value
// of iscbranch/isubranch/isubranch_nottaken for branch_never insts.
// This causes the compressor to use the branch predictor for these
// branches, which affects the compression efficiency.
union dcti_info_u gen_dcti_info_v317(uint32_t instr)
{
    union dcti_info_u dinfo;
    dinfo.u32 = 0x0;

    // if (spix_sparc_iop_isdcti(iop)) {
    if (rz_is_dcti(instr)) {
        dinfo.flags.isdcti = true;
        // if (spix_sparc_iop_isbranch(iop)) {
        if (rz_is_branch(instr)) {
            dinfo.flags.isbranch = true;
            // if (spix_sparc_iop_iscbranch(iop)) {
            if (rz_is_cbranch(instr) || rz_is_ubranch_never(instr)) {
                dinfo.flags.iscbranch = true;
            } else { /* not cbranch */
                dinfo.flags.isubranch = true;
                uint32_t b = (instr & UBRANCH_OPCODE_MASK);
                if ((b == FBN_OPCODE_BITS) || (b == BN_OPCODE_BITS)) {
                    dinfo.flags.isubranch_nottaken = true;
                }
            } /* cbranch? */

            dinfo.flags.annul_flag = (instr >> 29) & 1;

            // specific branch type and disp
            // if ((instr & BPcc_OPCODE_MASK) == BPcc_OPCODE_BITS) {
            if (rz_is_bpcc(instr)) {
                dinfo.flags.isBPcc = 1;
                // } else if ((instr & BPR_OPCODE_MASK) == BPR_OPCODE_BITS) {
            } else if (rz_is_bpr(instr)) {
                dinfo.flags.isBPR = 1;
                // } else if ((instr & FBfcc_OPCODE_MASK) == FBfcc_OPCODE_BITS) {
            } else if (rz_is_fbfcc(instr)) {
                dinfo.flags.isFBfcc = 1;
                // } else if ((instr & FBPfcc_OPCODE_MASK) == FBPfcc_OPCODE_BITS) {
            } else if (rz_is_fbpfcc(instr)) {
                dinfo.flags.isFBPfcc = 1;
                // } else if ((instr & Bicc_OPCODE_MASK) == Bicc_OPCODE_BITS) {
            } else if (rz_is_bicc(instr)) {
                dinfo.flags.isBicc = 1;
            } else {
                /* other branch instr? */
                fprintf(stderr, "ERROR: rz3iu: gen_dcti_info: invalid branch instr %08x\n", instr);
                dinfo.u32 = 0;
                return dinfo;
            }

        } else { /* not branch */
            // if ((instr & CALL_OPCODE_MASK) == CALL_OPCODE_BITS) {
            if (rz_is_call(instr)) {
                dinfo.flags.iscall = 1;
            } else {
                dinfo.flags.isindirect = 1;
                // is ret if rd=0, rs1=31, imm=8
                // is retl if rd=0, rs1=15, imm=8
                // is "call indirect" otherwise if rd is 15
                int32_t rs1 = (instr >> 14) & 0x1f;
                int32_t rd = (instr >> 25) & 0x1f;
                uint32_t simm13 = instr & 0x1fff;
                if ((rd == 0) && (simm13 == 0x8)) {
                    if (rs1 == 15) {
                        dinfo.flags.is_retl = 1;
                    } else if (rs1 == 31) {
                        dinfo.flags.is_ret = 1;
                    } else {
                        // do nothing
                    }
                } // else - do nothing
            }
        } // branch?
        // } else if ((iop == SPIX_SPARC_IOP_DONE)||(iop == SPIX_SPARC_IOP_RETRY)) {
    } else if (rz_is_done(instr) || rz_is_retry(instr)) {
        dinfo.flags.is_done_retry = 1;
    } else {
        // do nothing
    } // instr type?

    return dinfo;
} // gen_dcti_info


void rz3iu_icache_data::gen_target(uint64_t pc)
{
    if (dinfo.flags.isbranch) {
        if (dinfo.flags.isBPcc) {
            target = pc + BPcc_DISP(instr);
        } else if (dinfo.flags.isBPR) {
            target = pc + BPR_DISP(instr);
        } else if (dinfo.flags.isFBfcc) {
            target = pc + FBfcc_DISP(instr);
        } else if (dinfo.flags.isFBPfcc) {
            target = pc + FBPfcc_DISP(instr);
        } else if (dinfo.flags.isBicc) {
            target = pc + Bicc_DISP(instr);
        } else {
            assert(0);
        }
    } else if (dinfo.flags.iscall) {
        target = pc + CALL_DISP(instr);
    }
} // gen_target()


